﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Reflection;

namespace Query2Object
{
    /// <summary>
    /// Load DatatContext Dll
    /// </summary>
    public class DllLoader
    {
        /// <summary>
        /// Try to load context dll
        /// 尝试获取上下文dll
        /// </summary>
        /// <param name="type">DataBase Type(数据库类型的枚举)</param>
        /// <returns>DataContext Instance(数据上下文对象)</returns>
        public static IDbContext TryLoadContext(qDbType type)
        {
            IDbContext Rs = null;
            try
            {
                string path = GetBase();
                string dllName = GetAssName(type);
                path += dllName + ".dll";
                Assembly op = GetAssembly(path, dllName);
                Type CtxType = GetContextType(op);
                Rs = (IDbContext)Activator.CreateInstance(CtxType);
            }
            catch (AppDomainUnloadedException ex)
            {
                throw new ContextDllLoadException("Load running base error!", ex);
            }
            catch (NotSupportedException ex)
            {
                throw new ContextDllLoadException("Not supported yet!", ex);
            }
            catch (ArgumentNullException ex)
            {
                throw new ContextDllLoadException("Dll path is null!", ex);
            }
            catch (FileLoadException ex)
            {
                throw new ContextDllLoadException("Dll load error!", ex);
            }
            catch (FileNotFoundException ex)
            {
                throw new ContextDllLoadException("dll file no exist!may be don't support this database", ex);
            }
            catch (BadImageFormatException ex)
            {
                throw new ContextDllLoadException("load type error!", ex);
            }
            catch (ReflectionTypeLoadException ex)
            {
                throw new ContextDllLoadException("load type error!maybe wrong dll version", ex);
            }
            catch (Exception ex)
            {
                throw new ContextDllLoadException("UnKown error", ex);
            }
            return Rs;
        }
        
        private static Type GetContextType(Assembly op)
        {
            Type[] tps = tps = op.GetTypes();
            Type CtxType = null;
            foreach (Type tp in tps)
            {
                if (tp.Name.EndsWith("Context"))
                {
                    CtxType = tp;
                    break;
                }
            }
            return CtxType;
        }

        private static string GetBase()
        {
            string path = AppDomain.CurrentDomain.BaseDirectory;
            DirectoryInfo rundir = new DirectoryInfo(path);
            DirectoryInfo[] Subs = rundir.GetDirectories();
            for (int i = 0; i < Subs.Length; i++)
            {
                if (Subs[i].Name.ToLower().Equals("bin"))
                {
                    path += "Bin\\";
                    break;
                }
            }
            return path;
        }

        private static Assembly GetAssembly(string path,string dllName)
        {
            string Name = "";
            bool have = false;
            Assembly[] ass = AppDomain.CurrentDomain.GetAssemblies();
            Assembly op = null;
            foreach (Assembly asm in ass)
            {
                Name = asm.ToString().ToLower();
                try
                {
                    if (Name.Contains(dllName.ToLower()))
                    {
                        op = asm;
                        have = true;
                        break;
                    }
                }
                catch (NotSupportedException ex)
                {
                    throw new Exception("Load File Failt!", ex);
                }
            }
            if (!have)
            {
                op = Assembly.LoadFile(path);
            }
            return op;
        }

        private static string GetAssName(qDbType type)
        {
            string dllName = "";
            switch (type)
            {
                case qDbType.SqlServer2000:
                    dllName = "SqlServer2000";
                    break;
                case qDbType.SqlServer2005:
                    dllName = "SqlServer2005";
                    break;
                case qDbType.Access:
                    dllName = "Access";
                    break;
                case qDbType.CommonOdbc:
                    dllName = "CommonOdbc";
                    break;
                case qDbType.Mysql:
                    dllName = "Mysql";
                    break;
                case qDbType.Oracle:
                    dllName = "Oracle";
                    break;
                default:
                    throw new NotSupportedException("Not support this feature");
            }
            return dllName;
        }
    }
}
